/**
 * 数字字符串的校验结果
 */
export enum Float {
  /** 有效的 */
  VALID = 10,
  /** 无效的 */
  UNVALID = 11,
  /** 非安全的数 */
  UNSAFE = 12,
  /** 大于最大值 */
  GREATER_THAN_MAX_VALUE = 13,
  /** 小于最小值 */
  LESS_THAN_MIN_VALUE = 14,
  /** 精度过大 */
  EXCESSIVE_PRECISION = 15
}

export type FloatMessage = Partial<Record<Float, string>>

/**
 * 数字字符串的校验结果
 */
export enum Integer {
  /** 有效的 */
  VALID = 20,
  /** 无效的 */
  UNVALID = 21,
  /** 非安全的数 */
  UNSAFE = 22,
  /** 大于最大值 */
  GREATER_THAN_MAX_VALUE = 23,
  /** 小于最小值 */
  LESS_THAN_MIN_VALUE = 24,
  /** 是浮点数 */
  FLOAT = 25
}

export type IntegerMessage = Partial<Record<Integer, string>>

export enum NumberMessageExtension {
  EXTENSION_1
}

interface NumberRule {
  /**
   * 允许为 null、undefined 或 ''
   * @default true
   */
  nullable?: boolean
  /**
   * 最小值限制
   */
  min?: number
  /**
   * 最大值限制
   */
  max?: number
  /**
   * 是否进行安全整数校验
   * @default true
   * @see https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Reference/Global_Objects/Number/isSafeInteger
   */
  safe?: boolean
}

export interface FloatRule extends NumberRule {
  /** 校验/格式化的目标为浮点数 */
  mode: 'float'
  /** 精度 */
  precision?: number
}

export interface IntegerRule extends NumberRule {
  /** 校验/格式化的目标为整数 */
  mode: 'integer'
}

/**
 * 数字正则
 */
const NumberRegExp = /^-?(\d|[1-9]\d+|\d\.\d+|[1-9]\d+\.\d+)$/

/**
 * 浮点数正则
 */
const FloatNumberRegExp = /^-?(\d\.\d+|[1-9]\d*\.\d+)$/

function isObject(value: any): value is object {
  return Object.prototype.toString.call(value) === '[object Object]'
}

function isNullable(value: any): value is null | undefined {
  return value === null || value === undefined
}

function messageTemplateCompilation<Rule extends FloatRule | IntegerRule>(template: string, rule: Rule): string {
  return template.replace(/{(min|max|precision)}/g, (match, key: keyof Rule) => {
    return (rule[key] as string) ?? match
  })
}

//------------------------------------------------------------------- 浮点 -------------------------------------------------------------------//

const PrecisionRegExpPool: { [k: number]: RegExp } = {}

function getPrecisionRegExp(precision?: number) {
  if (typeof precision === 'number' && precision > 0 && !Number.isNaN(precision)) {
    if (!PrecisionRegExpPool[precision]) {
      PrecisionRegExpPool[precision] = new RegExp(`^-?\\d+\\.\\d{1,${precision}}$`)
    }
    return PrecisionRegExpPool[precision]
  }
  return void 0
}

/**
 * 浮点数校验函数
 * @param value 被校验的值
 */
export function floatValidator(value: any, rule: FloatRule): Float {
  if (rule.nullable !== false && (isNullable(value) || value === '')) return Float.VALID

  value = String(value)

  if (!NumberRegExp.test(value)) return Float.UNVALID

  const numberValue = parseFloat(value)

  const { min } = rule
  if ((min || min === 0) && numberValue < min) return Float.LESS_THAN_MIN_VALUE

  const { max } = rule
  if ((max || max === 0) && numberValue > max) return Float.GREATER_THAN_MAX_VALUE

  if (rule.safe !== false && !Number.isSafeInteger(parseInt(value))) return Float.UNSAFE

  const precisionRegExp = getPrecisionRegExp(rule.precision)
  if (precisionRegExp && FloatNumberRegExp.test(value) && !precisionRegExp.test(value)) return Float.EXCESSIVE_PRECISION

  return Float.VALID
}

/**
 * 浮点数校验结果的默认提示文字
 */
const defaultFloatMessage: Required<FloatMessage> = {
  [Float.VALID]: '',
  [Float.UNVALID]: '请输入数字',
  [Float.UNSAFE]: '请输入 ±(2^53 - 1) 区间整数',
  [Float.GREATER_THAN_MAX_VALUE]: '请输入 ≤{max} 的整数',
  [Float.LESS_THAN_MIN_VALUE]: '请输入 ≥{min} 的整数',
  [Float.EXCESSIVE_PRECISION]: '请将精度控制在 {precision} 位'
}

const extensionFloatMessage: Record<NumberMessageExtension, FloatMessage> = {
  [NumberMessageExtension.EXTENSION_1]: {}
}

function getFloatMessage(
  result: Float,
  rule: FloatRule,
  customMessage?: FloatMessage | NumberMessageExtension
): string {
  let text = defaultFloatMessage[result]
  if (!isNullable(customMessage)) {
    customMessage = isObject(customMessage) ? customMessage : extensionFloatMessage[customMessage]
    text = customMessage[result] ?? defaultFloatMessage[result]
  }
  return messageTemplateCompilation(text, rule)
}

//------------------------------------------------------------------- 整数 -------------------------------------------------------------------//

/**
 * 整数校验函数
 * @param value 被校验的值
 */
export function integerValidator(value: any, rule: IntegerRule): Integer {
  if (rule.nullable !== false && (isNullable(value) || value === '')) return Integer.VALID

  value = String(value)

  if (!NumberRegExp.test(value)) return Integer.UNVALID
  if (FloatNumberRegExp.test(value)) return Integer.FLOAT

  const numberValue = parseInt(value)

  const { min } = rule
  if ((min || min === 0) && numberValue < min) return Integer.LESS_THAN_MIN_VALUE

  const { max } = rule
  if ((max || max === 0) && numberValue > max) return Integer.GREATER_THAN_MAX_VALUE

  if (rule.safe !== false && !Number.isSafeInteger(numberValue)) return Integer.UNSAFE

  return Integer.VALID
}

/**
 * 整数校验结果的默认提示文字
 */
const defaultIntegerMessage: Required<IntegerMessage> = {
  [Integer.VALID]: '',
  [Integer.UNVALID]: '请输入整数',
  [Integer.UNSAFE]: '请输入 ±(2^53 - 1) 区间整数',
  [Integer.GREATER_THAN_MAX_VALUE]: '请输入 ≤{max} 的整数',
  [Integer.LESS_THAN_MIN_VALUE]: '请输入 ≥{min} 的整数',
  [Integer.FLOAT]: '请输入整数'
}

const extensionIntegerMessage: Record<NumberMessageExtension, IntegerMessage> = {
  [NumberMessageExtension.EXTENSION_1]: {}
}

function getIntegerMessage(
  result: Integer,
  rule: IntegerRule,
  customMessage?: IntegerMessage | NumberMessageExtension
): string {
  let text = defaultIntegerMessage[result]
  if (!isNullable(customMessage)) {
    customMessage = isObject(customMessage) ? customMessage : extensionIntegerMessage[customMessage]
    text = customMessage[result] ?? defaultIntegerMessage[result]
  }
  return messageTemplateCompilation(text, rule)
}
